﻿using ILRuntime.Mono.Cecil;
using ILRuntime.Runtime.Intepreter;
using Smart.Model.TABLE;
using System.Diagnostics;

namespace Smart.Model
{
    public static class GDecoder
    {
        public static int[] intValues;
        public static string[] stringValues;
        public static long[] longValues;
        public static int[] varIntValues;
        public static long[] varLongValues;
        public static string[] varStringValues;
        public static int pos;
        static byte[] contents;
        static int cnt;
        static int intValueLength;
        static int stringValueLength;
        static int longValueLength;

        public static int[] intValuesExtend = new int[128];
        public static string[] stringValuesExtend = new string[128];
        public static long[] longValuesExtend = new long[128];

        static TableData LoadTable(byte[] cs)
        {
            int cnt = Begin(cs);
            TableData gData = new TableData();
            gData.intValues = intValues;
            gData.stringValues = stringValues;
            gData.longValues = longValues;
            gData.id2offset = new System.Collections.Generic.Dictionary<int, TableHandle>(cnt);
            gData.handles = new TableHandle[cnt];
            gData.varIntValues = varIntValues;
            gData.varLongValues = varLongValues;
            gData.varStringValues = varStringValues;
            var id2offset = gData.id2offset;
            var handles = gData.handles;

            for (int i = 0; i < cnt; ++i)
            {
                var handle = new TableHandle
                {
                    data = gData,
                    intOffset = i * intValueLength,
                    stringOffset = i * stringValueLength,
                    longOffset = i * longValueLength,
                };
                handle.key = intValues[handle.intOffset];
                handles[i] = handle;
                id2offset.Add(handle.key, handle);
            }
            return gData;
        }

        public static TableData Load(byte[] cs, ITableLoader loader)
        {
            var tableData = LoadTable(cs);
            loader.Init(tableData);
            BindTableData(tableData, loader.CreateAndBind, loader.Bind);
            return tableData;
        }

        public static TableData Load(byte[] cs,System.Action<TableData> onInitialize,System.Func<TableHandle,object> handleFactory, System.Action<object,TableHandle> bind)
        {
            var tableData = LoadTable(cs);
            onInitialize(tableData);
            BindTableData(tableData, handleFactory, bind);
            return tableData;
        }

        static void BindTableData(TableData tableData, System.Func<TableHandle, object> createHandle, System.Action<object, TableHandle> bind)
        {
            var handles = tableData.handles;
            for (int i = 0, max = handles.Length; i < max; ++i)
            {
                if (i == 0)
                {
                    object ilHandle = createHandle(handles[i]);
                    handles[i].Value = ilHandle;
                }
                else
                {
                    if (handles[i - 1].Value is ILTypeInstance ilTypeInstance)
                    {
                        object ilHandle = ilTypeInstance.Clone();
                        bind(ilHandle, handles[i]);
                        handles[i].Value = ilHandle;
                    }
                    else
                    {
                        object ilHandle = createHandle(handles[i]);
                        handles[i].Value = ilHandle;
                    }
                }
            }
        }

        public static int Begin(byte[] cs)
        {
            pos = 0;
            contents = cs;
            varIntValues = null;
            varStringValues = null;
            varLongValues = null;

            cnt = contents.ReadInt(ref pos);
            if (cnt > 0)
                varIntValues = new int[cnt];
            for (int i = 0, max = cnt; i < max; ++i)
                varIntValues[i] = contents.ReadInt(ref pos);

            cnt = contents.ReadInt(ref pos);
            if (cnt > 0)
                varStringValues = new string[cnt];
            for (int i = 0, max = cnt; i < max; ++i)
                varStringValues[i] = contents.ReadString(ref pos);

            cnt = contents.ReadInt(ref pos);
            if (cnt > 0)
                varLongValues = new long[cnt];
            for (int i = 0, max = cnt; i < max; ++i)
                varLongValues[i] = contents.ReadLong(ref pos);

            intValueLength = contents.ReadInt(ref pos);
            stringValueLength = contents.ReadInt(ref pos);
            longValueLength = contents.ReadInt(ref pos);
            cnt = contents.ReadInt(ref pos);

            intValues = new int[contents.ReadInt(ref pos)];
            for(int i = 0,max = intValues.Length; i < max;++i)
            {
                intValues[i] = contents.ReadInt(ref pos);
            }

            stringValues = new string[contents.ReadInt(ref pos)];
            for (int i = 0, max = stringValues.Length; i < max; ++i)
            {
                stringValues[i] = contents.ReadString(ref pos);
            }

            longValues = new long[contents.ReadInt(ref pos)];
            for (int i = 0, max = longValues.Length; i < max; ++i)
            {
                longValues[i] = contents.ReadLong(ref pos);
            }

            return cnt;
        }
    }
}